﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using VNet.VisiSuite.VisiBoard.Models;

namespace VNet.VisiSuite.VisiBoard.Common.Helpers
{
    /// <summary>
    /// Smart reflection
    /// </summary>
    public static class SmartReflection
    {
        /// <summary>
        /// Clones the model.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objSource">The object source.</param>
        /// <param name="isFitting"></param>
        /// <returns></returns>
        public static T CloneModel<T>(T objSource) where T : ComparableBaseModel<T>
        {
            T objTarget = (T)CloneObject(objSource);

            if (objTarget != null)
                objTarget.OriginalObject = objSource;

            return objTarget;
        }

        /// <summary>
        /// Clones the object.
        /// </summary>
        /// <param name="objSource">The object source.</param>
        /// <returns></returns>
        public static object CloneObject(object objSource)
        {
            if (objSource == null)
                return null;

            Type typeSource = objSource.GetType();
            object objTarget = Activator.CreateInstance(typeSource);
            PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            foreach (PropertyInfo property in propertyInfo)
            {
                Type t = property.PropertyType;
                if (property.CanWrite && property.CanRead && !property.IsDefined(typeof(IgnoreClone), false))
                {
                    if (property.PropertyType.IsValueType || property.PropertyType.IsEnum ||
                        property.PropertyType == typeof(String))
                    {
                        property.SetValue(objTarget, property.GetValue(objSource, null), null);
                    }
                    else if (property.GetIndexParameters().Length > 0 && property.Name == "Item")
                    {
                        var list = objSource as System.Collections.IList;
                        var targetList = objTarget as System.Collections.IList;
                        if (list != null)
                            for (int i = 0; i < list.Count; i++)
                            {
                                var objIndex = new object[] { i };
                                var objPropertyValue = property.GetValue(objSource, objIndex);

                                targetList.Add(CloneObject(objPropertyValue));
                            }
                    }
                    else if (property.GetIndexParameters().Length == 0)
                    {
                        var objPropertyValue = property.GetValue(objSource, null);
                        property.SetValue(objTarget, objPropertyValue == null ? null : CloneObject(objPropertyValue), null);
                    }

                }
            }
            return objTarget;
        }

        public static bool IsCollectionEqual<T>(this IList<T> listA, IList<T> listB)
        {
            if (listA == null && listB == null)
                return true;
            else if (listA != null && listB != null)
            {
                if (listA.Count != listB.Count)
                    return false;

                var backupList = new List<T>();
                foreach (var item in listB)
                    backupList.Add(item);

                T foundItem = default(T);
                foreach (var a in listA)
                {
                    bool found = false;
                    foreach (var b in backupList)
                        if (a.Equals(b))
                        {
                            found = true;
                            foundItem = b;
                            break;
                        }

                    if (found) backupList.RemoveAt(backupList.IndexOf(foundItem));
                    else return false;
                }
                return true;
            }
            else
                return false;
        }

        public static bool IsValid(this IDataErrorInfo data)
        {
            Type typeSource = data.GetType();
            PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var isValid = true;
            foreach (PropertyInfo property in propertyInfo)
            {
                if (!property.PropertyType.IsValueType && !property.PropertyType.IsEnum && !(property.PropertyType == typeof(String)) && !property.IsDefined(typeof(IgnoreClone), false))
                {
                    continue;
                }
                else if (!string.IsNullOrEmpty(data[property.Name]))
                {
                    property.SetValue(data, property.GetValue(data, null), null);
                    isValid = false;
                }
            }
            return isValid;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class IgnoreClone : Attribute
    {
    }
}